/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ES2PANDAD_ENDPOINT_H
#define ES2PANDAD_ENDPOINT_H

#include "utils/json_builder.h"
#include "utils/json_parser.h"
#include "utils/logger.h"
#include "os/mutex.h"

#include <functional>
#include <string>
#include <optional>
#include <unordered_map>

namespace panda {
class JsonObject;
}  // namespace panda

namespace panda::es2panda::ls {

class Endpoint {

public:
    class MessageHandler{
    public:
        virtual ~MessageHandler() = default;
        
        // interface of handler
        virtual bool onCall(const std::string &method, 
                            panda::JsonObject &params,
                            panda::JsonObject::NumT id) = 0;
    };

    Endpoint(std::istream &in, std::ostream &out);
    ~Endpoint() = default;
    void HandleMessage(const std::string &message, MessageHandler &handler);
    bool Loop(MessageHandler &handler);

    void Reply(JsonObject::NumT &id, std::optional<JsonObjectBuilder> &&result) {
        if (result.has_value()) {
            SendMessage([&id, &result](JsonObjectBuilder &builder) {
                builder.AddProperty("jsonrpc", "2.0");
                builder.AddProperty("id", id);
                builder.AddProperty("reslut", std::move(result.value()).Build());
            });
        } else {
            // reply error code
            LOG(DEBUG, ES2PANDAD) << "error happened";
        }
    }

private:
    // read message
    bool ReadMessage(std::string &message);
    template <typename BuildFunction>
    void SendMessage(BuildFunction &&build) {
        JsonObjectBuilder builder;
        build(builder);
        auto message = std::move(builder).Build();
        LOG(DEBUG, ES2PANDAD) << "send message: " << message;
        out_ << "Content-Length: " << message.size() << "\r\n\r\n";
        out_ << message;
    }

    // utils
    std::string& Trim(std::string &line, std::string delimter = " \r\n");

    std::istream &in_;
    std::ostream &out_ [[maybe_unused]];
};

}

#endif
